﻿using FastExecutor.Base.Design;
using FastUtil.Base;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.WebPages.Razor;

//使用PreApplicationStartMethod注解的作用是在mvc应用启动之前执行操作
[assembly: PreApplicationStartMethod(typeof(FastExecutor.Base.Util.PluginUtil), "PreInitialize")]
namespace FastExecutor.Base.Util
{
    public class PluginUtil
    {
        private static DirectoryInfo PluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/Plugin"));
        private static DirectoryInfo TempPluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/PluginTemp"));
        private static IDictionary<string, Type> ControllerTypeDic = new Dictionary<string, Type>();
        private static List<Assembly> DllList = new List<Assembly>();
        /// <summary>
        /// 侦听文件系统。
        /// </summary>
        private static readonly FileSystemWatcher _FileSystemWatcher = new FileSystemWatcher();
        public static void PreInitialize()
        {
            Initialize();
        }

        public static void Initialize()
        {

            InitializeDll();
            InitializeCode();
            InitializeView();
        }
        public static void InitializeDll()
        {
            if (!PluginFolder.Exists)
            {
                PluginFolder.Create();
            }
            if (!TempPluginFolder.Exists)
            {
                TempPluginFolder.Create();
            }


            TempPluginFolder.Attributes = FileAttributes.Normal & FileAttributes.Directory;
            PluginFolder.Attributes = FileAttributes.Normal & FileAttributes.Directory;
            //清理临时文件。
            foreach (var file in TempPluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))
            {
                try
                {
                    File.SetAttributes(file.FullName, FileAttributes.Normal);
                    file.Delete();
                }
                catch (Exception e)
                {
                    throw new Exception("请检查IIS权限");
                }

            }
            //复制插件进临时文件夹。
            foreach (var plugin in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))
            {
                try
                {
                    string CopyFilePath = Path.Combine(TempPluginFolder.FullName, plugin.Name);
                    File.Copy(plugin.FullName, CopyFilePath, true);
                    File.SetAttributes(CopyFilePath, FileAttributes.Normal);
                }
                catch (Exception e)
                {
                    throw new Exception("请检查IIS权限");
                }
            }
        }
        public static void InitializeCode()
        {
            IEnumerable<Assembly> assemblies = GetProjectAssemblies();
            foreach (var assembly in assemblies)
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (type.GetInterface(typeof(IController).Name) != null && type.Name.Contains("Controller") && type.IsClass && !type.IsAbstract)
                    {
                        string Name = type.Name;
                        //如果有自定义的路由注解
                        if (type.IsDefined(typeof(System.Web.Mvc.RouteAreaAttribute), false))
                        {
                            var areaattribute = type.GetCustomAttributes(typeof(System.Web.Mvc.RouteAreaAttribute), false).FirstOrDefault();
                            Name = ((System.Web.Mvc.RouteAreaAttribute)areaattribute).AreaName;

                            if (type.IsDefined(typeof(System.Web.Mvc.RoutePrefixAttribute), false))
                            {
                                var rounteattribute = type.GetCustomAttributes(typeof(System.Web.Mvc.RoutePrefixAttribute), false).FirstOrDefault();
                                Name += ((System.Web.Mvc.RoutePrefixAttribute)rounteattribute).Prefix;
                            }
                            Name += "Controller";

                        }
                        if (!ControllerTypeDic.ContainsKey(Name))
                        {
                            ControllerTypeDic.Add(Name, type);
                        }
                    }
                }
                BuildManager.AddReferencedAssembly(assembly);
            }

        }
        public static void InitializeView()
        {
            IEnumerable<Assembly> assemblies = GetProjectAssemblies();
            foreach (var assembly in assemblies)
            {
                RazorBuildProvider.CodeGenerationStarted += (object sender, EventArgs e) =>
               {
                   RazorBuildProvider provider = (RazorBuildProvider)sender;
                   provider.AssemblyBuilder.AddAssemblyReference(assembly);
               };
            }

        }
        public static IEnumerable<Assembly> GetProjectAssemblies()
        {
            if (DllList.Count == 0)
            {
                IEnumerable<Assembly> assemblies = TempPluginFolder.GetFiles("*.dll", SearchOption.AllDirectories).Select(x => Assembly.LoadFile(x.FullName));
                foreach (Assembly item in assemblies)
                {
                    DllList.Add(item);
                }
            }
            return DllList;
        }
        public static Type GetControllerType(string ControllerName)
        {
            if (ControllerTypeDic.ContainsKey(ControllerName))
            {
                return ControllerTypeDic[ControllerName];
            }

            return null;
        }
        public static void StartWatch()
        {
            _FileSystemWatcher.Path = HostingEnvironment.MapPath("~/Plugin");
            _FileSystemWatcher.Filter = "*.dll";
            _FileSystemWatcher.Changed += _fileSystemWatcher_Changed;

            _FileSystemWatcher.IncludeSubdirectories = true;
            _FileSystemWatcher.NotifyFilter =
                NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;
            _FileSystemWatcher.EnableRaisingEvents = true;
        }
        private static void _fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            //这里通过修改webconfig文件的时间达到重启应用，加载项目dll的目的！
            File.SetLastWriteTime(HostingEnvironment.MapPath("~/Web.config"), System.DateTime.Now);
        }
    }
}
